package com.gitee.wsl.logger

import com.gitee.wsl.logger.context.EmptyLogContext
import com.gitee.wsl.logger.context.LogContext
import com.gitee.wsl.logger.context.plus

/**
 * This should be used more largely once https://kotlinlang.org/docs/whatsnew1620.html#prototype-of-context-receivers-for-kotlin-jvm is stable
 */
//interface LoggingContext {
//    val logger: MDCLogger
//}

/**
 * A simpler wrapper on top of [Logger] with better MDC support.
 */
data class MDCLogger(
    val logger: Logger,
    val staticMdc: LogContext = EmptyLogContext
) {

    fun log(
        level: LogLevel,
        message: Any?,
        throwable: Throwable? = null,
        meta: LogContext = EmptyLogContext,
    ) {
        logger.log(level, message?.toString() + mdcToString(staticMdc + meta), throwable)
    }

    inline fun debug(mdc: Map<String, Any> = emptyMap(), message: () -> String) {
        logger.d { message() + mdcToString(staticMdc + mdc) }
    }

    inline fun info(mdc: Map<String, Any> = emptyMap(), message: () -> String) {
        logger.i { message() + mdcToString(staticMdc + mdc) }
    }

    inline fun warning(ex: Throwable? = null, mdc: Map<String, Any> = emptyMap(), message: () -> String) {
        logger.w(ex) { message() + mdcToString(staticMdc + mdc) }
    }

    inline fun error(ex: Throwable? = null, mdc: Map<String, Any> = emptyMap(), message: () -> String) {
        logger.e(ex) { message() + mdcToString(staticMdc + mdc) }
    }

    fun mdcToString(mdc: Map<String, Any>): String {
        return if (mdc.isEmpty()) {
            ""
        } else {
            "\n" + mdc.map { (key, value) -> "    $key: $value" }.joinToString("\n")
        }
    }
}

suspend fun <T> MDCLogger.withMDC(mdc: Map<String, Any>, f: suspend (MDCLogger) -> T): T {
    val logger = this.copy(staticMdc = this.staticMdc + mdc)
    return f(logger)
}
